using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;

namespace CS_raytracer
{
    public class CCamera
    {
        
        private double FOV;
        private double HFOV;
        private double VFOV;

        private CVector direction = new CVector();
        private CVector right = new CVector();
        private CVector up = new CVector();

        private int currentPixel;
        private int maxPixels;

        List<Thread> m_WorkerThreads = new List<Thread>();
        
        private Object thisLock = new Object();

        private CScene scene;
        private CColor[] Renderpixels;

        public double GetFOV()
        {
            if (Constants.ViewportHeight > Constants.ViewportWidth)
            {
                return HFOV / Math.Acos(0.0f) * 90.0f;
            }
            else
            {
                return VFOV / Math.Acos(0.0f) * 90.0f;
            }

        }
        public void SetFOV(double _FOV)
        {
            FOV = _FOV;
            if (Constants.ViewportHeight > Constants.ViewportWidth)
            {
                HFOV = Math.Acos(0.0f) / 90.0f * FOV;
                VFOV = (double)(Constants.ViewportHeight) / Constants.ViewportWidth * HFOV;
            }
            else
            {
                VFOV = Math.Acos(0.0f) / 90.0f * FOV;
                HFOV = (double)(Constants.ViewportWidth) / Constants.ViewportHeight * VFOV;
            }
        }



        void RenderScene_ToHDR()
        {
            direction = LookAt - Position;
            direction.Normalize();
            right = direction.CrossProduct(new CVector(0.0f, 1.0f, 0.0f));
            up = right.CrossProduct(direction);
            currentPixel = 0;
            maxPixels = (Constants.RenderHeight*Constants.RenderWidth);
            for (int i = 0; i < Constants.maxThreads; ++i)
            {
                m_WorkerThreads.Add(new Thread(new ThreadStart(this.WorkerThreadFunction)));
            }
            for (int i = 0; i < Constants.maxThreads; ++i)
            {
                m_WorkerThreads[i].Start();
            } 
            while (true)
            {
                bool allDead = true;
                for (int i=0; i<Constants.maxThreads;++i)
                {
                    allDead = allDead && !m_WorkerThreads[i].IsAlive;
                }
                if (allDead)
                {
                    break;
                }
            }
            m_WorkerThreads.Clear();
        }

        private void WorkerThreadFunction()
        {
            int p=getWorkPixel();
            while (p < maxPixels)
            {
                RenderRay_ToHDR(p, direction, right, up);
                p = getWorkPixel();
            }
        }
        
        int getWorkPixel()
        {
            int result;
            lock (thisLock)
            {
                result = currentPixel;
                currentPixel++;
            }
            return result;
        }

        private void RenderRay_ToHDR(int p, CVector direction, CVector right, CVector up)
        {
            int x = p % Constants.RenderWidth;
            int y = (p - x) / Constants.RenderWidth;
            /*if ((x == Constants.RenderWidth / 2) && (y == Constants.RenderHeight / 2))
                System.Diagnostics.Debugger.Break();*/
            CRay view = new CRay();
            view.intensity = 1.0f;
            view.Origin = Position;
            view.BounceThreshold = Constants.MaxBounce;
            CColor ResultColor = new CColor();
            double angle_x = (x * HFOV / Constants.RenderWidth) - (HFOV / 2);
            double angle_y = (y * VFOV / Constants.RenderHeight) - (VFOV / 2);
            view.Direction = direction * Math.Cos(angle_x) * Math.Cos(angle_y) + right * Math.Sin(angle_x) * Math.Cos(angle_y) + up * Math.Sin(angle_y);
            view.Direction.Normalize();
            scene.RenderRay(view, ref ResultColor);
            Renderpixels[x + y * Constants.RenderWidth] = ResultColor;
        }
        public void RenderScene(CScene _scene, ref System.Drawing.Bitmap pixels, Form1 form)
        {
            scene = _scene;
            Renderpixels = new CColor[Constants.RenderWidth * Constants.RenderHeight];

            RenderScene_ToHDR();

            //pixels = new System.Drawing.Bitmap(Constants.ViewportWidth, Constants.ViewportHeight);

            double Exposure = Constants.Exposure; //.3395f;
            //double Exposure2 = 0.05395f;
            for (int y = Constants.ViewportHeight-1; y >=0; --y)
            {
                for (int x = 0; x < Constants.ViewportWidth; ++x)
                {
                    System.Drawing.Color col = System.Drawing.Color.FromArgb((int)Math.Floor(255*(1 - Math.Exp(-1.0f * Renderpixels[x + y * Constants.ViewportWidth].r * Exposure))), (int)Math.Floor(255*(1 - Math.Exp(-1.0f * Renderpixels[x + y * Constants.ViewportWidth].g * Exposure))), (int)Math.Floor(255*(1 - Math.Exp(-1.0f * Renderpixels[x + y * Constants.ViewportWidth].b * Exposure))));
                    pixels.SetPixel(Constants.ViewportWidth-x-1, Constants.ViewportHeight - y - 1, col);
/*                    if(y>0)
                        pixels.SetPixel(Constants.ViewportWidth - x - 1, Constants.ViewportHeight - y, System.Drawing.Color.FromArgb(1,1,1,1));*/
                }
            }
            //pixels.SetPixel(128, 128, System.Drawing.Color.White);
        }
        // 	void RenderScene_RAW(CScene scene,unsigned int *&pixels);
        // 	void RenderScene_ToHDR(CScene &scene,CColor * fpixels);
        // 	void Split_HDR(CColor *&pixels,CColor *&HDRpixels);
        // 	void Fuzzify_HDR(CColor *&HDRpixels);
        // 	void SpecialswitchX(const int &x,int &i,int &maxi);
        // 	void SpecialswitchY(const int &y,int &i,int &maxi);

        //	double BlurPotentialFunction(int x, int y);

        public CRenderableObject Camera_GetObject(CScene Scene, int x, int y)
        {
            return null;
        }

        public CVector Position;
        public CVector LookAt;
        
        public CCamera()
        {
            Position = new CVector();
            LookAt = new CVector();
        }
    }
}
